﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace MessageServer.utilities
{
    [AttributeUsage(AttributeTargets.Field)]
    public class NextT : System.Attribute
    {
        //used to inject the next element
    }

    [AttributeUsage(AttributeTargets.Field)]
    public class PrevT : System.Attribute
    {
        //used to inject the previous element
    }

    internal class DLLElem<T> : IDisposable where T : IDisposable
    {
        internal String _handle;
        internal T _elem;
        internal DLLElem<T> _next;
        internal DLLElem<T> _previous;

        public void Dispose()
        {
            _elem.Dispose();
        }

        internal void injectIntoT()
        {
            Type type = typeof(T);
            foreach (FieldInfo fld in type.GetFields(BindingFlags.Instance|BindingFlags.NonPublic|BindingFlags.Public))
            {
                if ((_next != null) && fld.IsDefined(typeof(NextT), true))
                    fld.SetValue(_elem, _next._elem);
                else if ((_previous != null) && (fld.IsDefined(typeof(PrevT), true)))
                    fld.SetValue(_elem, _previous._elem);
            }
        }
    }

    //This is not Thread Safe and has to be used only within a single thread.
    public class Pipeline<T> : IEnumerator<T> where T : IDisposable
    {
        DLLElem<T> _head;
        DLLElem<T> _tail;

        DLLElem<T> _current;

        public Pipeline()
        {
        }

        //TODO: for some reason first element is not returned.
        public bool MoveNext()
        {
            if (_current == null) _current = _head;
            if (_current != null) _current = _current._next; 
            return (_current != null);
        }

        public void Reset()
        {
            _current = _head;
        }


        private DLLElem<T> create(String handle, T elem)
        {
            DLLElem<T> t = new DLLElem<T>();
            t._elem = elem;
            t._handle = handle;

            return t;
        }

        public void addFirst(String handle, T elem)
        {
            DLLElem<T> t = create(handle, elem);
            if (_head == null) { _head = t; _tail = t; }
            else { t._next = _head; t.injectIntoT(); _head.injectIntoT(); _head = t; }
            t.injectIntoT();
        }

        public void addLast(String handle, T elem)
        {
            DLLElem<T> t = create(handle, elem);
            if (_tail == null) { _tail = t; _head = t; }
            else { t._previous = _tail; _tail._next = t; _tail.injectIntoT(); _tail = t; }
            t.injectIntoT();
        }

        public void addBefore(String childhandle, String handle, T elem)
        {
            DLLElem<T> t = create(handle, elem);
            DLLElem<T> child = getDLLElem(childhandle);
            if (child != null)
            {
                if (child._previous != null) { child._previous._next = t; child._previous.injectIntoT();  }
                t._next = child;
                t._previous = child._previous;
                child._previous = t;
                t.injectIntoT();
                child.injectIntoT();
            }
            else
                throw new Exception("Child Element not present");
        }

        public void addAfter(String parenthandle, String handle, T elem)
        {
            DLLElem<T> t = create(handle, elem);
            DLLElem<T> parent = getDLLElem(parenthandle);
            if (parent != null)
            {
                t._next = parent._next;
                t._previous = parent;
                if (parent._next != null) { parent._next._previous = t; }
                parent._next = t;
            }
            else
                throw new Exception("Parent Element not present");

        }

        public void remove(String handle)
        {
            DLLElem<T> t = getDLLElem(handle);
            if (t != null)
            {
                t._previous._next = t._next._next;
                t._next._previous = t._previous._next;
                t.Dispose();
            }
        }

        public T first { get { if (_head != null) return _head._elem; else return default(T); } }
        public T last { get { if (_tail != null) return _tail._elem; else return default(T); } }
        public T next { get { MoveNext(); return Current; } }

        public T get(String handle) 
        { 
            DLLElem<T> t = getDLLElem(handle);
            if (t != null) return t._elem;

            return default(T);
        }

        private DLLElem<T> getDLLElem(String handle)
        {
            DLLElem<T> t = _head;
            while (t != null)
            {
                if (t._handle == handle) return t;
                t = t._next;
            }

            return null;
        }

        public T Current
        {
            get
            {
                //if (_current == null) _current = _head;
                if (_current == null) return default(T);
                return _current._elem;
            }
        }

        public void Dispose()
        {
        }

        object System.Collections.IEnumerator.Current
        {
            get
            {
                //if (_current == null) _current = _head;
                if (_current == null) return default(T);
                return _current._elem;
            }
        }

        public IEnumerator<T> GetEnumerator()
        {
            return this;
        }
    }
}
